package com.sun.common.utils;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.sun.common.excetion.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import java.math.BigDecimal;
import java.util.*;

import static com.sun.common.constants.Constant.SSQ_FILE_PATH;
import static com.sun.common.constants.Constant.SSQ_OFFICIAL_WEBSITE_URL;

@Slf4j
public class SsqDataUtil {

    /**
     * 获取开始期号
     */
    public static String getStartIssue() {
        List<String> readList = FileUtil.readUtf8Lines(SSQ_FILE_PATH);

        String startIssue = "";
        if(CollectionUtils.isNotEmpty(readList)) {
            // 获取第一行数据（降序）
            String firstLine = readList.get(0);
            // 获取保存的最新期号
            startIssue = firstLine.split("\\|")[1];
        } else {
            startIssue = "03001";
        }
        log.info("开始期号：{}", startIssue);
        return startIssue;
    }

    /**
     * 获取最新期号
     */
    public static String getLatestIssue() {
        // 中国福利彩票官网
        Document doc = JsoupUtil.getDocument(SSQ_OFFICIAL_WEBSITE_URL);

        // 最新一期期号
        String latestIssue = "";
        if(doc != null) {
            Elements latestIssueElements = doc.select("[class=ssqQh-dom]");
            latestIssue = latestIssueElements.get(0).text().substring(2);
        }
        log.info("最新期号：{}", latestIssue);
        return latestIssue;
    }

    /**
     * 查询最新数据
     * @param startIssue 开始期号
     * @param latestIssue 最新期号
     * @return 最新数据列表
     */
    public static List<String> queryLatestData(String startIssue, String latestIssue) {
        // 判断是否为最新数据
        if(StringUtils.equalsIgnoreCase(startIssue, latestIssue)) {
            log.warn("已经是最新数据！");
            return Collections.emptyList();
        }

        if(calcDiffIssue(startIssue, latestIssue) <= 100) {
            return queryDataByOfficialWebsite(startIssue, latestIssue);
        }
        return queryDataBy500LotteryWebsite(startIssue, latestIssue);
    }

    /**
     * 计算期数差
     */
    public static int calcDiffIssue(String startIssue, String latestIssue) {
        return new BigDecimal(latestIssue).subtract(new BigDecimal(startIssue)).intValue();
    }

    /**
     * 通过福彩官网获取最新数据
     *
     * @param startIssue 开始期号
     * @param latestIssue 最新期号
     * @return 区间所有数据
     */
    public static List<String> queryDataByOfficialWebsite(String startIssue, String latestIssue) {
        List<String> list = new ArrayList<>();
        try {
            // 获取数据
            startIssue = String.valueOf(Integer.parseInt(startIssue) + 1);
            String pageUrl = getPageUrl(startIssue, latestIssue);
            log.info("通过【福彩官网】获取最新数据...\n访问地址：{}", pageUrl);
            Document doc = JsoupUtil.getDocument(pageUrl);
            assert doc != null;
            Elements bodyElements = doc.select("body");
            String homePageText = bodyElements.text();
            JSONObject homePageJson = JSONObject.parseObject(homePageText);

            // 数据
            JSONArray resultArray = homePageJson.getJSONArray("result");
            for (int i = 0; i < resultArray.size(); i++) {
                // 单注
                JSONObject single = resultArray.getJSONObject(i);

                // 开奖日期-前10位
                String lotteryDate = single.getString("date").substring(0, 10);
                // 期号-去掉前2位
                String issueNo = single.getString("code").substring(2);
                // 红球
                String redBall = single.getString("red");
                // 篮球
                String blueBall = single.getString("blue");

                JSONArray prizeGrades = single.getJSONArray("prizegrades");
                JSONObject jsonObject = prizeGrades.getJSONObject(0);
                // 一等奖个数
                String firstNum = jsonObject.getString("typenum");
                // 一等奖奖金
                String firstMoney = convertSeparator(jsonObject.getString("typemoney"));
                // 总奖池
                String poolMoney = convertSeparator(single.getString("poolmoney"));

                // 开奖日期|期号|红球号码+蓝球号码|一等奖个数*一等奖奖金|总奖池
                String combination = lotteryDate + "|" + issueNo + "|" + redBall + "+" + blueBall
                        + "|" + firstNum + "*" + firstMoney + "|" + poolMoney;
                list.add(combination);
            }
        } catch (Exception e) {
            throw new BusinessException("Z0001", "通过福彩官网获取最新一期期号发生错误！");
        }
        log.info("获取数据完成...");
        return list;
    }

    /**
     * 获取访问地址
     *
     * @param start 开始期号
     * @param end 结束期号
     * @return 访问地址
     */
    private static String getPageUrl(String start, String end) {
        return "https://www.cwl.gov.cn/cwl_admin/front/cwlkj/search/kjxx/findDrawNotice" +
                "?name=ssq&issueCount=&issueStart=20" + start + "&issueEnd=20" + end +
                "&dayStart=&dayEnd=&pageNo=1&pageSize=100&week=&systemType=PC";
    }

    /**
     * 将千位分隔转换成万位分隔
     *
     * @param oneThousandStr 千位分隔字符串
     * @return 万位分隔字符串
     */
    public static String convertSeparator(String oneThousandStr) {
        String replace = oneThousandStr.replace(",", "");

        int length = replace.length();
        if(length <= 4) {
            return replace;
        }

        // 从后往前数，每4个字符添加一个逗号
        StringBuilder thousandStr = new StringBuilder();
        int i = length - 1;
        while (i >= 0) {
            thousandStr.insert(0, replace.charAt(i));
            if ((length - i) % 4 == 0 && length - i != length) {
                thousandStr.insert(0, ',');
            }
            i--;
        }
        return thousandStr.toString();
    }

    /**
     * 获取历史数据
     *
     * @param startIssue 开始期号
     * @param endIssue 结束期号
     * @return 区间所有数据
     */
    public static List<String> queryDataBy500LotteryWebsite(String startIssue, String endIssue) {
        List<String> list = new ArrayList<>();
        try {
            String start = String.valueOf(Integer.parseInt(startIssue) + 1);
            String url = "https://datachart.500.com/ssq/history/newinc/history.php?start=" + start + "&end=" + endIssue;
            log.info("通过【500彩票网】获取最新数据...\n访问地址：{}", url);
            Document doc = JsoupUtil.getDocument(url);
            if(doc != null) {
                Elements trs = doc.select("tbody[id=tdata]").select("tr");
                trs.forEach(e -> {
                    Elements tds = e.select("td");
                    list.add(buildCombination(tds));
                });
            }
        } catch (Exception e) {
            throw new BusinessException("Z0002", "通过500彩票网获取最新一期期号发生错误！");
        }
        log.info("获取数据完成...");
        return list;
    }

    /**
     * 构建组合信息
     *
     * @param elements 元素列表
     * @return 组合信息
     */
    public static String buildCombination(Elements elements) {
        // 期号
        String issueNo = elements.get(0).text();

        // 红球号码
        String one = elements.get(1).text();
        String two = elements.get(2).text();
        String three = elements.get(3).text();
        String four = elements.get(4).text();
        String five = elements.get(5).text();
        String six = elements.get(6).text();
        // 蓝球
        String blueBall = elements.get(7).text();

        // 奖池
        String jackpot = SsqDataUtil.convertSeparator(elements.get(9).text());

        // 一等奖个数
        String firstPrizeNo = elements.get(10).text();
        // 一等奖奖金
        String firstPrizeBonus = SsqDataUtil.convertSeparator(elements.get(11).text());

        // 开奖日期
        String lotteryDate = elements.get(15).text();

        return lotteryDate + "|" + issueNo + "|" +
                one + "," + two + "," + three + "," + four + "," + five +  "," + six + "+" + blueBall
                + "|" + firstPrizeNo + "*" + firstPrizeBonus + "|" + jackpot;
    }

    /**
     * 分析最新数据
     * @param latestDataList 最新数据列表
     */
    public static List<String> analysisLatestData(List<String> latestDataList) {
        List<String> ssqList = new ArrayList<>();
        try {
            // 清空文件内容
            for (String ssq : latestDataList) {
                // 第二个|索引
                int secondPipeIndex = ssq.indexOf("|", ssq.indexOf("|") + 1);
                // +索引
                int secondPlusIndex = ssq.indexOf("+", ssq.indexOf("|"));
                // 组合
                String combination = ssq.substring(secondPipeIndex + 1, secondPlusIndex);
                // 组合列表
                List<Integer> combinationList = new ArrayList<>();
                for (String s : combination.split(",")) {
                    combinationList.add(Integer.parseInt(s));
                }
                // AC值
                int acValue = calcACValue(combinationList);
                // 连号数
                int serialNumber = calcSerialNumber(combinationList);
                // 和值
                int sumValue = calcSum(combinationList);
                // 奇偶比
                String oddEvenRatio = calcOddEven(combinationList);
                // 分布
                String distribution = calcDistribution(combinationList);
                // 写出内容
                String outputContent = getOutputContent(ssq, acValue, serialNumber, sumValue, oddEvenRatio, distribution);
                ssqList.add(outputContent);
            }
        } catch (Exception e) {
            throw new BusinessException("Z0003", "进行数据分析时，发生错误！");
        }
        log.info("数据分析完成...");
        return ssqList;
    }

    /**
     * 计算AC值
     *
     * @param combinationList 单注组合
     * @return AC值
     */
    public static int calcACValue(List<Integer> combinationList) {
        int num1 = combinationList.get(0);
        int num2 = combinationList.get(1);
        int num3 = combinationList.get(2);
        int num4 = combinationList.get(3);
        int num5 = combinationList.get(4);
        int num6 = combinationList.get(5);

        Set<Integer> set = new HashSet<>();
        set.add(num6 - num1);
        set.add(num6 - num2);
        set.add(num6 - num3);
        set.add(num6 - num4);
        set.add(num6 - num5);

        set.add(num5 - num1);
        set.add(num5 - num2);
        set.add(num5 - num3);
        set.add(num5 - num4);

        set.add(num4 - num1);
        set.add(num4 - num2);
        set.add(num4 - num3);

        set.add(num3 - num1);
        set.add(num3 - num2);

        set.add(num2 - num1);

        // AC值公式：AC = R - (N - 1) -> N = 6
        return set.size() - (6 - 1);
    }

    /**
     * 过滤连号
     *
     * @param combination 红球组合
     */
    public static List<Integer> filterSerialNumber(List<Integer> combination, List<String> serialNoList) {
        if(CollectionUtils.isNotEmpty(serialNoList) && CollectionUtils.isNotEmpty(combination)) {
            int serialNumber = calcSerialNumber(combination);
            if(serialNoList.contains(String.valueOf(serialNumber))) {
                return combination;
            }
        }
        return Collections.emptyList();
    }

    /**
     * 计算连号数
     */
    public static int calcSerialNumber(List<Integer> combinationList) {
        if(CollectionUtils.isEmpty(combinationList)) {
            return 0;
        }
        // 所有连号组合
        List<String> serialNumberList = new ArrayList<>();
        for (int i = 0; i < combinationList.size() - 1; i++) {
            String serialNumber = "";
            int front = combinationList.get(i);
            int after = combinationList.get(i + 1);
            if((front + 1) == after) {
                serialNumber = front + "-" + after;

                for (String s : serialNumberList) {
                    String[] split = s.split("-");
                    String smallNo = split[0];
                    String bigNo = split[split.length - 1];
                    if(StringUtils.equals(String.valueOf(front), bigNo)) {
                        serialNumber = smallNo + "-" + after;
                    }
                }
                serialNumberList.add(serialNumber);
            }
        }
        // 最大连号数量
        int maxCount = 0;
        for (String s : serialNumberList) {
            String[] split = s.split("-");
            int diff = Integer.parseInt(split[1]) - Integer.parseInt(split[0]) + 1;
            if(diff > maxCount) {
                maxCount = diff;
            }
        }
        return maxCount;
    }

    /**
     * 过滤和值
     * 历史排名：105 > 90 > 89 > 103 > 91 > 101 > 102 > 99 > 106 > 111 > 94
     *
     * @param combination 红球组合
     */
    public static List<Integer> filterSum(List<Integer> combination, List<String> sumList) {
        if(CollectionUtils.isNotEmpty(sumList) && CollectionUtils.isNotEmpty(combination)) {
            int calcSum = combination.stream().mapToInt(Integer::intValue).sum();
            if(sumList.contains(String.valueOf(calcSum))) {
                return combination;
            }
        }
        return Collections.emptyList();
    }

    /**
     * 计算和值
     */
    public static int calcSum(List<Integer> combinationList) {
        return combinationList.stream().mapToInt(Integer::intValue).sum();
    }

    /**
     * 过滤奇偶比
     * 历史数据：3:3 > 4:2 > 2:4
     *
     * @param combination 全部红球组合
     */
    public static List<Integer> filterOddEvenRatio(List<Integer> combination, List<String> oddEvenRatioList) {
        if(CollectionUtils.isNotEmpty(oddEvenRatioList) && CollectionUtils.isNotEmpty(combination)) {
            String calcOddEven = calcOddEven(combination);
            if(StringUtils.isNotBlank(calcOddEven) && oddEvenRatioList.contains(calcOddEven)) {
                return combination;
            }
        }
        return Collections.emptyList();
    }

    /**
     * 计算奇偶比
     */
    public static String calcOddEven(List<Integer> combinationList) {
        // 奇数的数量
        int oddNumbers = 0;
        // 偶数的数量
        int evenNumbers = 0;
        for (Integer integer : combinationList) {
            // 奇数
            if (integer % 2 != 0) {
                oddNumbers++;
            }
            // 偶数
            if (integer % 2 == 0) {
                evenNumbers++;
            }
        }
        return oddNumbers + ":" + evenNumbers;
    }

    /**
     * 过滤分布
     * 历史数据：2:2:2 > 3:1:2 > 3:1:2
     *
     * @param combination 全部红球组合
     */
    public static List<Integer> filterDistribution(List<Integer> combination, List<String> distributionList) {
        if(CollectionUtils.isNotEmpty(distributionList) && CollectionUtils.isNotEmpty(combination)) {
            String distribution = calcDistribution(combination);
            if(StringUtils.isNotBlank(distribution) && distributionList.contains(distribution)) {
                return combination;
            }
        }
        return Collections.emptyList();
    }

    /**
     * 计算分布
     *
     */
    public static String calcDistribution(List<Integer> combinationList) {
        int count1 = 0;
        int count2 = 0;
        int count3 = 0;
        for (Integer value : combinationList) {
            if (value <= 11) {
                count1++;
            }
            if (value > 11 && value <= 22) {
                count2++;
            }
            if (value > 22 && value <= 33) {
                count3++;
            }
        }
        return count1 + ":" + count2 + ":" + count3;
    }

    /**
     * 获取写出内容
     */
    private static String getOutputContent(String ssq, int acValue, int serialNumber, int sumValue,
                                           String oddEvenRatio, String distribution) {
        return ssq +
                "|AC=" + ((acValue >= 10) ? acValue : acValue + " ") +
                "|SerialNumber=" + serialNumber +
                "|SUM=" + ((sumValue >= 100) ? sumValue : sumValue + " ") +
                "|OddEvenRatio=" + oddEvenRatio +
                "|Distribution=" + distribution +
                "|";
    }

    /**
     * 写出数据
     * @param analysisDataList 分析后的数据列表
     */
    public static void writeLatestData(List<String> analysisDataList) {
        if(CollectionUtils.isNotEmpty(analysisDataList)) {
            // 读取保存数据
            List<String> readList = FileUtil.readUtf8Lines(SSQ_FILE_PATH);
            // 合并数据-因为数据为倒序所以需要合并重写
            analysisDataList.addAll(readList);
            // 写入数据
            FileUtil.writeUtf8Lines(analysisDataList, SSQ_FILE_PATH);
            log.info("获取最新数据完成！");
        }
    }

    /**
     * 生成全量红球组合数据<br>
     * 规则：从1~33中随机选择6个不重复数字
     *
     * @return 全量红球组合数据
     */
    public static List<List<Integer>> generateAllRedCombinations() {
        List<List<Integer>> combinations = new ArrayList<>();
        for (int i = 1; i < 29; i++) {
            for (int j = 2; j < 30; j++) {
                if (i >= j) {
                    continue;
                }
                for (int k = 3; k < 31; k++) {
                    if (j >= k) {
                        continue;
                    }
                    for (int l = 4; l < 32; l++) {
                        if (k >= l) {
                            continue;
                        }
                        for (int m = 5; m < 33; m++) {
                            if (l >= m) {
                                continue;
                            }
                            for (int n = 6; n < 34; n++) {
                                if (m >= n) {
                                    continue;
                                }
                                List<Integer> combination = new ArrayList<>();
                                combination.add(i);
                                combination.add(j);
                                combination.add(k);
                                combination.add(l);
                                combination.add(m);
                                combination.add(n);
                                combinations.add(combination);
                            }
                        }
                    }
                }
            }
        }
        return combinations;
    }

}
